package com.guigu.yh.springcloud.service.inpl;

import cn.hutool.core.util.IdUtil;
import com.guigu.yh.springcloud.service.PaymentService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * 付款服务实现
 *
 * @author 张颖辉（zyh）
 * @version 1.0.0
 * @date 2022/08/22
 */
@Service
public class PaymentServiceImpl implements PaymentService {

    @Value("${server.port}")
    private String serverPort;

    @Override
    public String paymentInfo_OK(Integer id) {
        //正常返回没有任何问题
        return "线程池：" + Thread.currentThread().getName() + " paymentInfo_OK, id :" + id + "\t" + "O(∩_∩)O哈哈~";
    }

    @Override
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOut_Handler", //出现异常时，要执行的应急函数
            commandProperties = {
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") //超过两秒就执行fallbackMethod的逻辑
            })
    public String paymentInfo_TimeOut(Integer id) {
        //异常：故意超时
        int timeNumber = 3;
        System.out.println("paymentInfo_TimeOut 被执行了");
        try {
            TimeUnit.SECONDS.sleep(timeNumber);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池：" + Thread.currentThread().getName() + " paymentInfo_TimeOut, id :" + id + "\t" + "O(∩_∩)O哈哈~ 耗时(秒)：" + timeNumber;
    }

    /**
     * 服务生产者 降级 兜底方法
     */
    public String paymentInfo_TimeOut_Handler(Integer id) {
        return "线程池：" + Thread.currentThread().getName() + " 系统繁忙，请稍后再试，服务生产者|降级|兜底方法，serverPort:" + serverPort;
        //http://localhost:8008/payment/hystrix/timeout/222
        //线程池：HystrixTimer-1 系统繁忙，请稍后再试，服务生产者|降级|兜底方法，serverPort:8008
    }


    @Override
    @HystrixCommand(fallbackMethod = "paymentInfo_Err_Handler" //出现异常时，要执行的应急函数
    )
    public String paymentInfo_Err(Integer id) {
        //异常：故意报错
        int i = 10 / 0;
        return "线程池：" + Thread.currentThread().getName() + " paymentInfo_Err, id :" + id + "\t" + "O(∩_∩)O哈哈~ ";
    }

    /**
     * 服务生产者 降级 兜底方法
     */
    public String paymentInfo_Err_Handler(Integer id) {
        return "线程池：" + Thread.currentThread().getName() + " 系统错误，请稍后再试，服务生产者|降级|兜底方法，serverPort:" + serverPort;
        //http://localhost:8008/payment/hystrix/err/222
        //线程池：hystrix-PaymentServiceImpl-2 系统错误，请稍后再试，服务生产者|降级|兜底方法，serverPort:8008
    }

    //服务熔断，这里用的是 Hystrix，将来趋势是用 Sentinel 来替代 Hystrix
    @Override
    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback", //出现异常时，要执行的应急函数
            commandProperties = {
                    @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),  //是否开启断路器
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),   //请求次数阀值，默认20
                    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),  //快照时间窗口（时间范围），如默认最近20秒内
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"), //失败百分比阀值达到多少后跳闸，默认50
            })
    /**
     * 总结上面的配置：
     * 当服务满足熔断条件时，会进入断路状态，后面的请求直接进入应急函数，直到满足恢复服务的条件才会回到正常状态
     * 服务熔断条件：当10*1000毫秒内达到10次以上的请求，且其中60%是失败的（每次异常都会触发执行应急函数）。则此接口会触发服务熔断，之后的请求不会调用当前服务主函数，而是会直接走应急函数（即使参数没有问题）。
     * 服务恢复条件：经过一段时间（默认5秒）之后进入半熔断状态，部分请求根据规则调用当前服务主函数，如果调用成功（失败率下降）则会恢复服务。
     * 熔断的三个状态：熔断开启（全部请求直接调用应急函数）。熔断关闭（该怎么调用就怎么调用）。熔断半开（部分请求根据规则调用当前服务）
     * */
    public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
        if (id < 0) {
            throw new RuntimeException("*****id 不能负数");
        }
        String serialNumber = IdUtil.simpleUUID();

        return Thread.currentThread().getName() + "\t" + "调用成功,流水号：" + serialNumber;
    }

    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) {
        return "id 不能负数，请稍候再试,(┬＿┬)/~~     id: " + id;
    }

}
